﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace MX.Core.ORM
{
    public static class ConnectionHelper
    {

        class Provider
        {
            public string Dll { get; set; }
            public string Class { get; set; }
        }


        /*
              <add name="Odbc Data Provider" invariant="System.Data.Odbc" description=".Net Framework Data Provider for Odbc" type="System.Data.Odbc.OdbcFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
     <add name="OleDb Data Provider" invariant="System.Data.OleDb" description=".Net Framework Data Provider for OleDb" type="System.Data.OleDb.OleDbFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
     <add name="OracleClient Data Provider" invariant="System.Data.OracleClient" description=".Net Framework Data Provider for Oracle" type="System.Data.OracleClient.OracleClientFactory, System.Data.OracleClient, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
     <add name="SqlClient Data Provider" invariant="System.Data.SqlClient" description=".Net Framework Data Provider for SqlServer" type="System.Data.SqlClient.SqlClientFactory, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
     <add name="SQL Server CE Data Provider" invariant="Microsoft.SqlServerCe.Client" description=".NET Framework Data Provider for Microsoft SQL Server 2005 Mobile Edition" type="Microsoft.SqlServerCe.Client.SqlCeClientFactory, Microsoft.SqlServerCe.Client, Version=9.0.242.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91" />
     <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=5.0.2.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
            */
        static Dictionary<string, Provider> providerConfig = new Dictionary<string, Provider>();
        static ConnectionHelper()
        {
            providerConfig["System.Data.SqlClient"] = new Provider()
            {
                Class = "System.Data.SqlClient.SqlClientFactory",
                Dll = "System.Data.dll"
            };
            providerConfig["MySql.Data.MySqlClient"] = new Provider
            {
                Class = "MySql.Data.MySqlClient.MySqlClientFactory",
                Dll = "MySql.Data.dll"
            };

        }
        static string coreFile = Assembly.GetCallingAssembly().CodeBase;


        static ConcurrentDictionary<int, DbProviderFactory> providerFactoryCache = new ConcurrentDictionary<int, DbProviderFactory>();

        /// <summary>
        /// 获取一个连接池对象
        /// </summary>
        /// <param name="database"></param>
        /// <returns></returns>
		static public DbConnection GetConnection(object database)
        {
            int dbKey = Convert.ToInt32(database);
            var connString = DatabaseConfig.GetConnectionString(dbKey);
            DbProviderFactory providerFactory = providerFactoryCache.GetOrAdd(dbKey, (key) =>
            {
                if (!providerConfig.ContainsKey(connString.ProviderName))
                {
                    throw new Exception($"数据库ProviderName尚未配置：{connString.ToString()}");
                }
                Type factoryType = null;
                foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    factoryType = assembly.GetType(providerConfig[connString.ProviderName].Class);
                    if (factoryType != null) break;
                }
                if (factoryType == null)
                {
                    string ddlPath = Path.Combine(Path.GetDirectoryName(new Uri(coreFile).LocalPath), providerConfig[connString.ProviderName].Dll);
                    if (File.Exists(ddlPath))
                    { 
                        factoryType = AppDomain.CurrentDomain.Load(AssemblyName.GetAssemblyName(ddlPath)).GetType(providerConfig[connString.ProviderName].Class);
                    }
                    else
                    {
                        throw new Exception($"数据库{connString.ProviderName}的DLL尚未引用,文件不存在{ddlPath}");
                    }
                }
                if (factoryType == null) throw new Exception($"数据库{connString.ProviderName}的DLL尚未引用,无法加载{providerConfig[connString.ProviderName]}");
                FieldInfo field = factoryType.GetField("Instance", BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Public);
                if ((FieldInfo)null != field && field.FieldType.IsSubclassOf(typeof(DbProviderFactory)))
                {
                    DbProviderFactory dbProviderFactory = field.GetValue(null) as DbProviderFactory;
                    if (dbProviderFactory != null)
                    {
                        return dbProviderFactory;
                    }
                    else
                    {
                        throw new Exception($"{providerConfig[connString.ProviderName]}类Instance字段获取错误，不是标准DbProviderFactory");
                    }
                }
                else
                {
                    throw new Exception($"{providerConfig[connString.ProviderName]}类没有Instance字段，不是标准DbProviderFactory");
                }
            });
            DbConnection connection = providerFactory.CreateConnection();
            connection.ConnectionString = connString.ConnectionString;
            connection.Open();
            return connection;

        }
    }
}
